/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.action.core.linkedObjects;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.util.StringUtil;
import com.blandware.atleap.model.core.*;
import com.blandware.atleap.service.core.ContentResourceManager;
import com.blandware.atleap.service.core.PageManager;
import com.blandware.atleap.webapp.action.core.BaseAction;
import com.blandware.atleap.webapp.util.core.LinkedObjectsInfoContainer;
import com.blandware.atleap.webapp.util.core.WebappConstants;
import com.blandware.atleap.webapp.util.core.WebappUtil;
import org.apache.commons.validator.GenericValidator;
import org.apache.struts.Globals;
import org.apache.struts.action.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * <p>Exposes lists of content field values and menu items which refer to object of given type and
 * with given identifier
 * </p>
 * <p><a href="ViewLinkedObjectsAction.java.html"><i>View Source</i></a></p>
 * <p/>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.16 $ $Date: 2007/12/18 18:12:01 $
 * @struts.action path="/core/linkedObjects/view"
 * scope="request"
 * validate="false"
 * roles="core-linkedObjects-view"
 * @struts.action-forward name="viewLinkedObjects"
 * path=".core.linkedObjects.view"
 */
public final class ViewLinkedObjectsAction extends BaseAction {
	/**
	 * @param mapping  The ActionMapping used to select this instance
	 * @param form     The optional ActionForm bean for this request (if any)
	 * @param request  The HTTP request we are proceeding
	 * @param response The HTTP response we are creating
	 * @return an ActionForward instance describing where and how
	 *         control should be forwarded, or null if response
	 *         has already been completed
	 */
	public ActionForward execute(ActionMapping mapping, ActionForm form,
	                             HttpServletRequest request, HttpServletResponse response) throws Exception {

        LinkedObjectsInfoContainer container;
        if (request.getSession().getAttribute(WebappConstants.LINKED_OBJECTS_INFO_CONTAINER_KEY) != null) {
            container = (LinkedObjectsInfoContainer) request.getSession().getAttribute(WebappConstants.LINKED_OBJECTS_INFO_CONTAINER_KEY);
        } else {
            container = new LinkedObjectsInfoContainer();
        }

        if ("true".equalsIgnoreCase(request.getParameter("justView"))) {
            container.clearDeleteUpdateFields();
        }

        Long id = null;
        List ids = null;
        boolean multiple = false;
        if (container.getObjectsIds() != null) {
            ids = container.getObjectsIds();
            multiple = true;
        } else if ( !GenericValidator.isBlankOrNull(request.getParameter("id")) ) {
			id = Long.valueOf(request.getParameter("id"));
		} else if (container.getObjectId() != null) {
			id = container.getObjectId();
		} else {
			if ( log.isWarnEnabled() ) {
				log.warn("Object ID is not specified. Returning to index...");
			}
			return mapping.findForward("admin");
		}

        if (!multiple) {
            container.setObjectId(id);
        }

        String type = request.getParameter("type");
		if ( GenericValidator.isBlankOrNull(type) ) {
			type = container.getObjectType();
		}
		if ( !LinkedObjectsInfoContainer.TYPE_RESOURCE.equalsIgnoreCase(type)
                && !LinkedObjectsInfoContainer.TYPE_PAGE.equalsIgnoreCase(type) ) {
			type = LinkedObjectsInfoContainer.TYPE_RESOURCE;
		}
        container.setObjectType(type);

        String redirectUrl = null;
        if (!GenericValidator.isBlankOrNull(request.getParameter("redirectUrl"))) {
            redirectUrl = request.getParameter("redirectUrl");
        } else if (!GenericValidator.isBlankOrNull(container.getRedirectUrl())) {
            redirectUrl = container.getRedirectUrl();
        }
        container.setRedirectUrl(redirectUrl);
        request.setAttribute(WebappConstants.LINKED_OBJECTS_REDIRECT_URL_KEY, redirectUrl);

        List infos;
        if (multiple) {
            boolean someNotFound = false;
            boolean someFound = false;
            infos = new ArrayList();
            for (Iterator i = ids.iterator(); i.hasNext(); ) {
                Long curId = (Long) i.next();
                CFVsAndMenuItemsInfo cfvsAndMenuItemsInfo = getCfvAndMenuItemInfos(curId, type, request);
                if (cfvsAndMenuItemsInfo == null) {
                    someNotFound = true;
                } else {
                    someFound = true;
                    infos.add(cfvsAndMenuItemsInfo);
                }
            }
            if (!someFound) {
                // nothing was found at all
				ActionMessages errors = new ActionMessages();
                String msgKey;
                String errorKey;
                if (LinkedObjectsInfoContainer.TYPE_PAGE.equals(type)) {
                    msgKey = "pageNotFound";
                    errorKey = "core.page.errors.notFoundMultiple";
                } else {
                    msgKey = "resourceNotFound";
                    errorKey = "core.contentResource.errors.notFoundMultilpe";
                }
                errors.add(msgKey, new ActionMessage(errorKey));
				saveErrors(request, errors);
				return mapping.findForward("admin");
            }
            if (someNotFound) {
                // at least one was not found...
				ActionMessages errors = new ActionMessages();
                String errorKey;
                if (LinkedObjectsInfoContainer.TYPE_PAGE.equals(type)) {
                    errorKey = "core.page.errors.notFoundSome";
                } else {
                    errorKey = "core.contentResource.errors.notFoundSome";
                }
                errors.add("someNotFound", new ActionMessage(errorKey));
				saveMessages(request, errors);
            }
        } else {
            CFVsAndMenuItemsInfo cfvsAndMenuItemsInfo = getCfvAndMenuItemInfos(id, type, request);
            if (cfvsAndMenuItemsInfo == null) {
				ActionMessages errors = new ActionMessages();
                String msgKey;
                String errorKey;
                if (LinkedObjectsInfoContainer.TYPE_PAGE.equals(type)) {
                    msgKey = "pageNotFound";
                    errorKey = "core.page.errors.notFound";
                } else {
                    msgKey = "resourceNotFound";
                    errorKey = "core.contentResource.errors.notFound";
                }
                errors.add(msgKey, new ActionMessage(errorKey));
				saveErrors(request, errors);
				return mapping.findForward("admin");
            } else {
                infos = new ArrayList(1);
                infos.add(cfvsAndMenuItemsInfo);
            }
        }

        request.setAttribute(WebappConstants.LINKED_CONTENT_FIELD_VALUES_AND_MENU_ITEMS_INFOS_COLLECTION_KEY, infos);

        if (container.getUpdateAction() != null) {
            saveToken(request);
        }

        request.getSession().setAttribute(WebappConstants.LINKED_OBJECTS_INFO_CONTAINER_KEY, container);

        return mapping.findForward("viewLinkedObjects");
	}

    /**
     * Returns lists of infos about CFVs and menu items for object with
     * given ID.
     *
     * @param id        ID of object
     * @param type      type of object (page or resource)
     * @param request   request
     * @return infos or null if no object was found with given ID
     */
    private CFVsAndMenuItemsInfo getCfvAndMenuItemInfos(Long id, String type, HttpServletRequest request) {
        List linkedContentFieldValues = null;
        List linkedMenuItems = null;
        CFVsAndMenuItemsInfo cfVsAndMenuItemsInfo = null;
        boolean notFound = false;
        String objectInfo = null;
        if (LinkedObjectsInfoContainer.TYPE_RESOURCE.equalsIgnoreCase(type)) {
            ContentResourceManager contentResourceManager = (ContentResourceManager) getBean(Constants.CONTENT_RESOURCE_MANAGER_BEAN);
            ContentResource resource = contentResourceManager.retrieveContentResource(id);
            if (resource != null) {
                linkedContentFieldValues = new ArrayList(resource.getLinkedContentFieldValues());
                linkedMenuItems = new ArrayList(resource.getLinkedMenuItems());
                objectInfo = StringUtil.htmlEncode(resource.getUri().substring(Constants.RESOURCES_URI_PREFIX.length()));
            } else {
                notFound = true;
            }
        } else {
            PageManager pageManager = (PageManager) getBean(Constants.PAGE_MANAGER_BEAN);
            Page page = pageManager.retrievePage(id);
            if (page != null) {
                linkedContentFieldValues = new ArrayList(page.getLinkedContentFieldValues());
                linkedMenuItems = new ArrayList(page.getLinkedMenuItems());
                objectInfo = WebappUtil.getLocalizableInfo(page, request);
            } else {
                notFound = true;
            }
        }

        if (!notFound) {
            for ( ListIterator i = linkedContentFieldValues.listIterator(); i.hasNext(); ) {
                ContentFieldValue contentFieldValue = (ContentFieldValue) i.next();
                i.set(new CFVInfo(contentFieldValue, request));
            }
            for ( ListIterator i = linkedMenuItems.listIterator(); i.hasNext(); ) {
                MenuItem menuItem = (MenuItem) i.next();
                i.set(new MenuItemInfo(menuItem, request));
            }
            cfVsAndMenuItemsInfo = new CFVsAndMenuItemsInfo(objectInfo, linkedContentFieldValues, linkedMenuItems);
        }

        return cfVsAndMenuItemsInfo;
    }

    /**
	 * Helper class to expose information about linked content field value
	 */
	public static class CFVInfo {

		/**
		 * Information about owner of corresponding content field
		 */
		protected String fieldOwnerInfo;

		/**
		 * Identifier of owning content field
		 */
		protected String fieldIdentifier;

		/**
		 * Language of content field value
		 */
		protected String language;

		/**
		 * ID of owning content field
		 */
		protected Long fieldId;

		/**
		 * ID of content field value
		 */
		protected Long fieldValueId;

		/**
		 * Creates new instance of CFVInfo
		 *
		 * @param contentFieldValue Content field value to get info about
		 * @param request           HTTP servlet request we are currently proceeding
		 */
		public CFVInfo(ContentFieldValue contentFieldValue, HttpServletRequest request) {
			ContentField contentField = contentFieldValue.getContentField();
			this.fieldId = contentField.getId();
			this.fieldIdentifier = contentField.getIdentifier();
			this.fieldOwnerInfo = WebappUtil.getLocalizableInfo(contentField.getOwner(), request);
			this.language = contentFieldValue.getContentLocale().getIdentifier();
			this.fieldValueId = contentFieldValue.getId();
		}

        /**
         * Returns field owner info
         *
         * @return field owner info
         */
		public String getFieldOwnerInfo() {
			return fieldOwnerInfo;
		}

        /**
         * Sets field owner info
         *
         * @param fieldOwnerInfo field owner info to set
         */
		public void setFieldOwnerInfo(String fieldOwnerInfo) {
			this.fieldOwnerInfo = fieldOwnerInfo;
		}

        /**
         * Returns field identifier
         *
         * @return field identifier
         */
		public String getFieldIdentifier() {
			return fieldIdentifier;
		}

        /**
         * Sets field identifier
         *
         * @param fieldIdentifier to set
         */
		public void setFieldIdentifier(String fieldIdentifier) {
			this.fieldIdentifier = fieldIdentifier;
		}

        /**
         * Returns language
         *
         * @return language
         */
		public String getLanguage() {
			return language;
		}

        /**
         * Sets language
         *
         * @param language language to set
         */
		public void setLanguage(String language) {
			this.language = language;
		}

        /**
         * Returns field ID
         *
         * @return field ID
         */
		public Long getFieldId() {
			return fieldId;
		}

        /**
         * Sets field ID
         *
         * @param fieldId field ID to set
         */
		public void setFieldId(Long fieldId) {
			this.fieldId = fieldId;
		}

        /**
         * Returns field value ID
         *
         * @return field value ID
         */
		public Long getFieldValueId() {
			return fieldValueId;
		}

        /**
         * Sets field value ID
         *
         * @param fieldValueId field value ID to set
         */
		public void setFieldValueId(Long fieldValueId) {
			this.fieldValueId = fieldValueId;
		}

	}

	/**
	 * Helper class to expose information about linked menu item
	 */
	public static class MenuItemInfo {

		/**
		 * Title of menu item
		 */
		protected String title;

		/**
		 * Information about layer, on which this menu item has been created
		 */
		protected String ownerInfo;

		/**
		 * ID of menu item
		 */
		protected Long menuItemId;

		/**
		 * ID of menu item's owner
		 */
		protected Long ownerId;

		/**
		 * Whether or not associated menu item is dynamic
		 */
		protected boolean dynamic;

		/**
		 * Creates new instance of menu item info
		 *
		 * @param menuItem Menu item to get info about
		 * @param request  HTTP servlet request we are currently proceeding
		 */
		public MenuItemInfo(MenuItem menuItem, HttpServletRequest request) {
			Locale locale = (Locale) request.getSession().getAttribute(Globals.LOCALE_KEY);
			this.title = WebappUtil.getFieldValue(menuItem.getTitle(), locale.getLanguage(), request, true);
			this.ownerInfo = WebappUtil.getLocalizableInfo(menuItem.getOwner(), request);
			this.ownerId = null;
			if ( menuItem.getOwner() != null ) {
				this.ownerId = menuItem.getOwner().getId();
			}
			this.menuItemId = menuItem.getId();
			this.dynamic = menuItem.isDynamic();
		}

        /**
         * Returns title
         *
         * @return title
         */
		public String getTitle() {
			return title;
		}

        /**
         * Sets title
         *
         * @param title title to set
         */
		public void setTitle(String title) {
			this.title = title;
		}

        /**
         * Returns owner info
         *
         * @return owner info
         */
		public String getOwnerInfo() {
			return ownerInfo;
		}

        /**
         * Sets owner info
         *
         * @param ownerInfo owner info to set
         */
		public void setOwnerInfo(String ownerInfo) {
			this.ownerInfo = ownerInfo;
		}

        /**
         * Returns menu item ID
         *
         * @return menu item ID
         */
		public Long getMenuItemId() {
			return menuItemId;
		}

        /**
         * Sets menu item ID
         *
         * @param menuItemId menu item ID to set
         */
		public void setMenuItemId(Long menuItemId) {
			this.menuItemId = menuItemId;
		}

        /**
         * Returns owner ID
         *
         * @return owner ID
         */
		public Long getOwnerId() {
			return ownerId;
		}

        /**
         * Sets owner ID
         *
         * @param ownerId owner ID to set
         */
		public void setOwnerId(Long ownerId) {
			this.ownerId = ownerId;
		}

        /**
         * Return whether menu item is dynamic
         *
         * @return whether menu item is dynamic
         */
		public boolean isDynamic() {
			return dynamic;
		}

        /**
         * Sets whether menu item is dynamic
         *
         * @param dynamic whether menu item is dynamic
         */
		public void setDynamic(boolean dynamic) {
			this.dynamic = dynamic;
		}
	}

    /**
     * Helper class that contains info about both CFVs and menu items
     */
    public static class CFVsAndMenuItemsInfo {
        /**
         * Textual object info
         */
        protected String objectInfo;
        /**
         * List of CFV infos
         */
        protected List cfvInfos;
        /**
         * List of menu items infos
         */
        protected List menuItemInfos;

        /**
         * Constructor.
         *
         * @param objectInfo    object info
         * @param cfvInfos      CFV infos
         * @param menuItemInfos menu items infos
         */
        public CFVsAndMenuItemsInfo(String objectInfo, List cfvInfos, List menuItemInfos) {
            this.objectInfo = objectInfo;
            this.cfvInfos = cfvInfos;
            this.menuItemInfos = menuItemInfos;
        }

        /**
         * Returns object info.
         *
         * @return object info
         */
        public String getObjectInfo() {
            return objectInfo;
        }

        /**
         * Sets object info.
         *
         * @param objectInfo info to set
         */
        public void setObjectInfo(String objectInfo) {
            this.objectInfo = objectInfo;
        }

        /**
         * Returns CFV infos.
         *
         * @return CFV infos
         */
        public List getCfvInfos() {
            return cfvInfos;
        }

        /**
         * Sets CFV infos.
         *
         * @param cfvInfos  infos to set
         */
        public void setCfvInfos(List cfvInfos) {
            this.cfvInfos = cfvInfos;
        }

        /**
         * Returns menu item infos.
         *
         * @return menu item infos
         */
        public List getMenuItemInfos() {
            return menuItemInfos;
        }

        /**
         * Sets menu item infos.
         *
         * @param menuItemInfos infos to set
         */
        public void setMenuItemInfos(List menuItemInfos) {
            this.menuItemInfos = menuItemInfos;
        }
    }
}